package view_server

import (
	"bigdevops/src/cache"
	"bigdevops/src/common"
	"bigdevops/src/config"
	"bigdevops/src/models"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/openkruise/kruise-api/rollouts/v1alpha1"
	"go.uber.org/zap"
	appsv1 "k8s.io/api/apps/v1"
	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/types"
	"k8s.io/apimachinery/pkg/util/json"
	deploymentutil "k8s.io/kubectl/pkg/util/deployment"
	"sigs.k8s.io/yaml"
	"sort"
	"strconv"
	"time"
)

// 根据工单id获取发布节点列表的接口

func getCicdDeployStageListByOrderId(c *gin.Context) {

	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	// 校验一下 menu字段
	id := c.Param("id")

	// 先 去db中根据id找到这个user
	intVar, _ := strconv.Atoi(id)

	// 	数据库中拿到所有的发布阶段列表

	objStages, err := models.GetCicdDeployStageStatusByOrderId(intVar)
	if err != nil {
		sc.Logger.Error("去数据库中拿所有的cicd发布阶段错误",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("去数据库中拿所有的cicd发布阶段错误:%v", err.Error()), c)
		return
	}

	//// 遍历 role 准备menuIds 列表
	for index, obj := range objStages {
		obj := obj
		obj.IndexNum = fmt.Sprintf("发布阶段:[%v/%v] 副本数:%v", index+1, len(objStages), obj.Replicas)
		obj.FillFrontAllData()

		kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
		dbCluster, err := models.GetK8sClusterByName(obj.Cluster)
		if err != nil {
			msg := fmt.Sprintf("发布没找到这个集群:%v", err.Error())
			sc.Logger.Error(msg)
			common.FailWithMessage(msg, c)
			return

		}
		krSet := kc.GetClusterKruiseClientSetById(dbCluster.ID)
		if krSet == nil {
			msg := fmt.Sprintf("根据id找k8s集群KruiseClient缓存错误")
			sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
			common.FailWithMessage(msg, c)
			return
		}

		ctx1, cancel1 := common.GenTimeoutContext(dbCluster.ActionTimeoutSeconds)
		defer cancel1()
		rolloutName := fmt.Sprintf("%s-%s", common.CICD_ROLLOUT_NAME_PREFIX, obj.K8sAppName)
		k8sRollout, err := krSet.RolloutsV1alpha1().Rollouts(obj.K8sAppObj.Namespace).Get(ctx1, rolloutName, metav1.GetOptions{})
		// err为空说明一定是找到了 。再判断
		if err == nil {
			obj.RolloutMessage = k8sRollout.Status.Message
			if obj.CicdWorkOrderObj.CicdDeployItemObj.IsCanary {
				if k8sRollout.Status.Phase != v1alpha1.RolloutPhaseHealthy {
					obj.IfShowButtonCanApproveRollout = true
				}
			}

		}

	}

	resp := &ResponseResourceCommon{
		//Total: models.GetCicdDeployWorkOrderCount(), 因为是带了查询条件的 所以 count 不能是all 而是 过滤后的total

		Total: len(objStages),
		Items: objStages,
	}

	common.OkWithDetailed(resp, "ok", c)
}

// 根据工单id获取发布节点列表的接口

func getCicdDeployStagePodList(c *gin.Context) {

	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	// 校验一下 menu字段
	id := c.Param("id")

	// 先 去db中根据id找到这个user
	intVar, _ := strconv.Atoi(id)

	// 	数据库中拿到所有的发布阶段列表

	// 先 去db中根据id找到这个对象
	dbStage, err := models.GetCicdDeployStageStatusById(intVar)
	if err != nil {
		sc.Logger.Error("根据id找发布阶段实例错误", zap.Any("工单实例", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	err = dbStage.FillFrontAllData()
	if err != nil {
		msg := fmt.Sprintf("dbStage.FillFrontAllData错误 :%v", err)
		sc.Logger.Error(msg,
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	dbCluster, err := models.GetK8sClusterByName(dbStage.Cluster)
	if err != nil {
		msg := fmt.Sprintf("发布没找到这个集群:%v", err.Error())
		sc.Logger.Error(msg)
		common.FailWithMessage(msg, c)
		return

	}
	kSet := kc.GetClusterClientSetById(dbCluster.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群KubeClient缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	ctx1, cancel1 := common.GenTimeoutContext(dbCluster.ActionTimeoutSeconds)
	defer cancel1()

	// 获取pod列表

	labelSelector, _ := fields.ParseSelector(fmt.Sprintf("%v=%v",
		common.K8S_APP_LABEL_APP_NAME, dbStage.K8sAppName,
	))

	podItems, err := kSet.CoreV1().Pods(dbStage.K8sAppObj.Namespace).List(ctx1, metav1.ListOptions{
		LabelSelector: labelSelector.String(),
	})
	if err != nil {
		msg := fmt.Sprintf("根据发布阶段获取k8s集群 pod列表错误:%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", dbStage.Cluster), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}

	myPods := []*OnePod{}
	imageVerionMap := map[string]string{}
	for _, pod := range podItems.Items {
		myPod := podConvert(pod)
		imageVerionMap[myPod.ImageVersion] = ""
		myPods = append(myPods, myPod)
	}
	for version := range imageVerionMap {
		if version == dbStage.CicdWorkOrderObj.GitTag {
			imageVerionMap[version] = "red"
		} else {
			imageVerionMap[version] = "green"
		}
	}

	for _, pod := range myPods {
		pod := pod
		pod.ImageVersionColor = imageVerionMap[pod.ImageVersion]

	}

	sort.SliceStable(myPods, func(i, j int) bool {
		return myPods[i].ImageVersion > myPods[j].ImageVersion

	})
	resp := &ResponseResourceCommon{
		//Total: models.GetCicdDeployWorkOrderCount(), 因为是带了查询条件的 所以 count 不能是all 而是 过滤后的total

		Total: len(myPods),
		Items: myPods,
	}

	common.OkWithDetailed(resp, "ok", c)
}

// 判断逻辑是 这个人在不在这个节点的 这几个user 多对多列表里都行
func cicdCheckUserCanActionDeployStage(node *models.StreeNode, reqUser *models.User) (pass bool) {
	if reqUser.AccountType == 2 {
		return true
	}
	// 先遍历本级 opsaadmin
	for _, user := range node.OpsAdmins {

		user := user
		if user.Username == reqUser.Username {
			pass = true
			return
		}
	}
	for _, user := range node.RdAdmins {

		user := user
		if user.Username == reqUser.Username {
			pass = true
			return
		}
	}
	for _, user := range node.RdMembers {

		user := user
		if user.Username == reqUser.Username {
			pass = true
			return
		}
	}
	return

}

// stage 和集群是1:1 所以这里都是单集群发布
func actionCicdDeployStage(c *gin.Context) {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	id := c.Param("id")

	// 先 去db中根据id找到这个对象
	intVar, _ := strconv.Atoi(id)
	dbStage, err := models.GetCicdDeployStageStatusById(intVar)
	if err != nil {
		sc.Logger.Error("根据id找发布阶段实例错误", zap.Any("工单实例", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	err = dbStage.FillFrontAllData()
	if err != nil {
		msg := fmt.Sprintf("dbStage.FillFrontAllData错误 :%v", err)
		sc.Logger.Error(msg,
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	// 获取操作人
	userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	dbUser, err := models.GetUserByUserName(userName)
	if err != nil {
		sc.Logger.Error("通过token解析到的userName去数据库中找User失败",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error()), c)
		return
	}
	// 校验这个人是否在可执行表里面
	pass := cicdCheckUserCanActionDeployStage(dbStage.CicdWorkOrderObj.CicdDeployItemObj.StreeNodeObj, dbUser)
	if !pass {
		sc.Logger.Error("服务树节点权限校验未通过", zap.Any("reqNode", dbStage.CicdWorkOrderObj.CicdDeployItemObj.StreeNodeObj), zap.Error(err))
		common.Req403WithWithMessage("服务树节点权限校验未通过", c)
		return

	}

	// 前期就创建这个rollout 对象
	depName := fmt.Sprintf("%v-%v", dbStage.K8sAppName, common.CICD_DEPLOY_NAME_BY_ORDER)
	if dbStage.CicdWorkOrderObj.CicdDeployItemObj.IsCanary {
		// 新增或更新这个rollout 对象

		// 把strategy从表中yaml 解析回来

		var canaryStrategy *v1alpha1.CanaryStrategy
		err = yaml.Unmarshal([]byte(dbStage.CicdWorkOrderObj.CicdDeployItemObj.CanaryStrategyYamlString), &canaryStrategy)
		if err != nil {
			msg := fmt.Sprintf("解析canaryStrategy yaml出错:%v", err.Error())
			sc.Logger.Error(msg)
			common.FailWithMessage(msg, c)
			return
		}

		rollout := &v1alpha1.Rollout{
			Spec: v1alpha1.RolloutSpec{
				ObjectRef: v1alpha1.ObjectRef{
					WorkloadRef: &v1alpha1.WorkloadRef{
						APIVersion: "apps/v1",
						Kind:       "Deployment",
						Name:       depName,
					},
				},
				Strategy: v1alpha1.RolloutStrategy{
					Canary: canaryStrategy,
				},
			},
		}
		//rollout.Annotations = map[string]string{
		//	"rollouts.kruise.io/rolling-style": "partition",
		//}

		rollout.Namespace = dbStage.K8sAppObj.Namespace
		rollout.Name = fmt.Sprintf("%s-%s", common.CICD_ROLLOUT_NAME_PREFIX, dbStage.K8sAppName)
		// 从cache中通过 集群的id 拿到集群的clientSet
		kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
		dbCluster, err := models.GetK8sClusterByName(dbStage.Cluster)
		if err != nil {
			msg := fmt.Sprintf("发布没找到这个集群:%v", err.Error())
			sc.Logger.Error(msg)
			common.FailWithMessage(msg, c)
			return

		}
		krSet := kc.GetClusterKruiseClientSetById(dbCluster.ID)
		if krSet == nil {
			msg := fmt.Sprintf("根据id找k8s集群KruiseClient缓存错误")
			sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
			common.FailWithMessage(msg, c)
			return
		}

		ctx1, cancel1 := common.GenTimeoutContext(dbCluster.ActionTimeoutSeconds)
		defer cancel1()

		k8sRollout, err := krSet.RolloutsV1alpha1().Rollouts(dbStage.K8sAppObj.Namespace).Get(ctx1, rollout.Name, metav1.GetOptions{})
		if err != nil {
			if !errors.IsNotFound(err) {
				msg := fmt.Sprintf("在k8s集群KruiseClient找rollout网络错误:%v", err.Error())
				sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
				common.FailWithMessage(msg, c)
				return
			}
			ctx2, cancel2 := common.GenTimeoutContext(dbCluster.ActionTimeoutSeconds)
			defer cancel2()

			// 到这里说明没有 就要创建
			k8sRollout, err = krSet.RolloutsV1alpha1().Rollouts(dbStage.K8sAppObj.Namespace).Create(ctx2, rollout, metav1.CreateOptions{})
			if err != nil {
				msg := fmt.Sprintf("在k8s集群KruiseClient创建rollout网络错误:%v", err.Error())
				sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
				common.FailWithMessage(msg, c)
				return
			}

		} else {
			ctx3, cancel3 := common.GenTimeoutContext(dbCluster.ActionTimeoutSeconds)
			defer cancel3()
			k8sRollout.Spec = rollout.Spec
			_, err = krSet.RolloutsV1alpha1().Rollouts(dbStage.K8sAppObj.Namespace).Update(ctx3, k8sRollout, metav1.UpdateOptions{})
			if err != nil {
				msg := fmt.Sprintf("在k8s集群KruiseClient更新rollout网络错误:%v", err.Error())
				sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
				common.FailWithMessage(msg, c)
				return
			}

		}

	}

	// 几个问题
	// 1. k8sApp 如何传入
	// 2. 创建K8sInstance字段 怎么传入 这里也可以服用 k8sApp中定义好的
	// 在k8sApp 下面创建k8sInstance

	// 拼接K8sInstance字段
	reqK8sInstance := models.K8sInstance{

		//Name:          strings.ReplaceAll(dbStage.CicdWorkOrderObj.GitTag, ".", "-"),
		Name:    common.CICD_DEPLOY_NAME_BY_ORDER,
		UserID:  dbUser.ID,
		Cluster: dbStage.Cluster,
		ContainerCore: models.ContainerCore{
			Labels: []string{fmt.Sprintf("%s=%s", common.K8S_APP_LABEL_VERSION, dbStage.CicdWorkOrderObj.GitTag)},
		},

		Image: fmt.Sprintf("%v:%v", dbStage.CicdWorkOrderObj.CicdDeployItemObj.ImagePrefix, dbStage.CicdWorkOrderObj.GitTag),

		Replicas: dbStage.Replicas,
		K8sAppID: dbStage.K8sAppId,
	}

	err = reqK8sInstance.FillFrontAllData()
	if err != nil {
		msg := fmt.Sprintf("reqK8sInstance.FillFrontAllData出错:%v", err)
		sc.Logger.Error(msg, zap.Error(err))
		common.OkWithMessage(msg, c)
		return
	}

	// 进行是否存在的判断
	dbK8sInstance, err := models.GetK8sInstanceByK8sAppIdAndName(dbStage.K8sAppId, reqK8sInstance.Name)

	errMsg := ""
	if err == nil {
		reqK8sInstance.ID = dbK8sInstance.ID
		errMsg = updateK8sInstanceOneInner(c, reqK8sInstance)

	} else {
		errMsg = createK8sInstanceOneInner(c, reqK8sInstance)
	}

	// 在这里统一处理对象的更新
	dbStage.CicdWorkOrderObj.Status = common.WorkOrderInstanceStatusUnderAction

	if errMsg != "" {
		dbStage.Status = common.CICD_STAGE_DEPLOY_FAILED
		dbStage.DeployStatus = common.CICD_STAGE_DEPLOY_FAILED_CHINESE
		// 如果某个发布阶段失败了，那么工单就结束了
		dbStage.CicdWorkOrderObj.Status = common.WorkOrderInstanceStatusFinished
		dbStage.Finished = common.GORM_ENBALE_RES_YES
		dbStage.LastError = errMsg
	} else {
		dbStage.DeployStatus = common.CICD_STAGE_DEPLOY_SUCCESS_CHINESE
		dbStage.Status = common.CICD_STAGE_DEPLOY_SUCCESS
	}
	dbStage.ActionUserId = dbUser.ID
	dbStage.DeployTime = time.Now().Format("2006-01-02 15:04:05")

	// 这里更新发布到了哪个阶段
	dbStage.CicdWorkOrderObj.CurrentStageIndex = dbStage.StageIndex

	//dbStage.CicdWorkOrderObj.Status =

	// 如下统一更新数据库
	err = dbStage.CicdWorkOrderObj.UpdateOne()
	if err != nil {
		msg := fmt.Sprintf("更新工单的发布阶段状态出错:%v", err)
		sc.Logger.Error(msg, zap.Error(err))
		common.OkWithMessage(msg, c)
		return
	}

	err = dbStage.UpdateOne()
	if err != nil {
		msg := fmt.Sprintf("reqK8sInstance.FillFrontAllData出错:%v", err)
		sc.Logger.Error(msg, zap.Error(err))
		common.OkWithMessage(msg, c)
		return
	}
	if errMsg != "" {
		common.FailWithMessage(errMsg, c)
		return
	}
	common.OkWithMessage("发布成功", c)
	return

}

// 单一集群的回滚操作
func rollBackCicdDeployStage(c *gin.Context) {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	id := c.Param("id")

	// 先 去db中根据id找到这个对象
	intVar, _ := strconv.Atoi(id)
	dbStage, err := models.GetCicdDeployStageStatusById(intVar)
	if err != nil {
		sc.Logger.Error("根据id找发布阶段实例错误", zap.Any("工单实例", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	err = dbStage.FillFrontAllData()
	if err != nil {
		msg := fmt.Sprintf("dbStage.FillFrontAllData错误 :%v", err)
		sc.Logger.Error(msg,
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	// 获取操作人
	userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	dbUser, err := models.GetUserByUserName(userName)
	if err != nil {
		sc.Logger.Error("通过token解析到的userName去数据库中找User失败",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error()), c)
		return
	}
	// 校验这个人是否在可执行表里面
	pass := cicdCheckUserCanActionDeployStage(dbStage.CicdWorkOrderObj.CicdDeployItemObj.StreeNodeObj, dbUser)
	if !pass {
		sc.Logger.Error("服务树节点权限校验未通过", zap.Any("reqNode", dbStage.CicdWorkOrderObj.CicdDeployItemObj.StreeNodeObj), zap.Error(err))
		common.Req403WithWithMessage("服务树节点权限校验未通过", c)
		return

	}
	// 回滚操作
	// 1.获取这个Deployment set image
	// 2.获取所有它的rs
	// 3.遍历找到这个rs中revision最大的那个 回滚即可

	depName := fmt.Sprintf("%v-%v", dbStage.K8sAppName, common.CICD_DEPLOY_NAME_BY_ORDER)

	dbCluster, err := models.GetK8sClusterByName(dbStage.Cluster)
	if err != nil {
		msg := fmt.Sprintf("回滚没找到这个集群:%v", err.Error())
		sc.Logger.Error(msg)
		common.FailWithMessage(msg, c)
		return

	}

	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	kSet := kc.GetClusterClientSetById(dbCluster.ID)
	if kSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群kset缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}
	dbK8sApp, err := models.GetK8sAppById(int(dbStage.K8sAppId))
	if err != nil {
		msg := fmt.Sprintf("根据id找K8sApp错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	ctx1, canFun1 := common.GenTimeoutContext(dbCluster.ActionTimeoutSeconds)
	defer canFun1()
	depInK8s, err := kSet.AppsV1().Deployments(dbK8sApp.Namespace).Get(ctx1, depName, metav1.GetOptions{})
	if err != nil {
		// 非not found的错误 -->  网络错误
		msg := fmt.Sprintf("回滚版本前置获取deployment对象错误:%v", err.Error())
		err = fmt.Errorf("%v %w", msg, err)
		sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
		common.FailWithMessage(msg, c)
		return

	}
	// 拿到Deployment 的observedGeneration
	// 再list 出来 所有的rs
	rsList, err := kSet.AppsV1().ReplicaSets(dbK8sApp.Namespace).List(ctx1, metav1.ListOptions{})
	if err != nil {
		msg := fmt.Sprintf("回滚版本前置获取deployment的rslist 错误 :%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}
	var targetRs *appsv1.ReplicaSet
	// 找到上一个版本
	for _, rs := range rsList.Items {

		revision, ok := rs.GetAnnotations()["deployment.kubernetes.io/revision"]
		if !ok {
			continue
		}
		revisionInt, _ := strconv.ParseInt(revision, 10, 64)
		if revisionInt+1 == depInK8s.Status.ObservedGeneration {
			targetRs = &rs
			break
		}

	}
	if targetRs == nil {
		msg := fmt.Sprintf("回滚版本前置根据deployment的ObservedGeneration找到rs错误 ")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	// 生成patch

	// remove hash label before patching back into the deployment
	delete(targetRs.Spec.Template.Labels, appsv1.DefaultDeploymentUniqueLabelKey)
	var annotationsToSkip = map[string]bool{
		corev1.LastAppliedConfigAnnotation:       true,
		deploymentutil.RevisionAnnotation:        true,
		deploymentutil.RevisionHistoryAnnotation: true,
		deploymentutil.DesiredReplicasAnnotation: true,
		deploymentutil.MaxReplicasAnnotation:     true,
		appsv1.DeprecatedRollbackTo:              true,
	}
	// compute deployment annotations
	annotations := map[string]string{}
	for k := range annotationsToSkip {
		if v, ok := depInK8s.Annotations[k]; ok {
			annotations[k] = v
		}
	}
	for k, v := range targetRs.Annotations {
		if !annotationsToSkip[k] {
			annotations[k] = v
		}
	}

	// make patch to restore
	patchType, patch, _ := getDeploymentPatch(&targetRs.Spec.Template, annotations)

	patchOptions := metav1.PatchOptions{}

	ctx2, canFun2 := common.GenTimeoutContext(dbCluster.ActionTimeoutSeconds)
	defer canFun2()
	// Restore revision
	if _, err = kSet.AppsV1().Deployments(dbK8sApp.Namespace).Patch(ctx2, depName, patchType, patch, patchOptions); err != nil {
		msg := fmt.Sprintf("回滚版本patch deployment的出错 err:%v ", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	dbStage.Status = common.CICD_STAGE_ROLLBACK_SUCCESS
	err = dbStage.UpdateOne()
	if err != nil {
		msg := fmt.Sprintf("更新stage的数据库出错:%v", err)
		sc.Logger.Error(msg, zap.Error(err))
		common.OkWithMessage(msg, c)
		return
	}
	common.OkWithMessage("回滚成功", c)
	return

}

// 单一集群的rollout灰度操作
func approveRolloutCicdDeployStage(c *gin.Context) {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	id := c.Param("id")

	// 先 去db中根据id找到这个对象
	intVar, _ := strconv.Atoi(id)
	dbStage, err := models.GetCicdDeployStageStatusById(intVar)
	if err != nil {
		sc.Logger.Error("根据id找发布阶段实例错误", zap.Any("工单实例", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	err = dbStage.FillFrontAllData()
	if err != nil {
		msg := fmt.Sprintf("dbStage.FillFrontAllData错误 :%v", err)
		sc.Logger.Error(msg,
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(msg, c)
		return
	}

	// 获取操作人
	userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	dbUser, err := models.GetUserByUserName(userName)
	if err != nil {
		sc.Logger.Error("通过token解析到的userName去数据库中找User失败",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error()), c)
		return
	}
	// 校验这个人是否在可执行表里面
	pass := cicdCheckUserCanActionDeployStage(dbStage.CicdWorkOrderObj.CicdDeployItemObj.StreeNodeObj, dbUser)
	if !pass {
		sc.Logger.Error("服务树节点权限校验未通过", zap.Any("reqNode", dbStage.CicdWorkOrderObj.CicdDeployItemObj.StreeNodeObj), zap.Error(err))
		common.Req403WithWithMessage("服务树节点权限校验未通过", c)
		return

	}
	// 回滚操作
	// 1.获取这个rollout 对象
	// 2.模仿tools 生成patch  D:\nyy_work\go_path\src\github.com\openkruise\kruise-tools\pkg\internal\polymorphichelpers\objectapprover.go
	// 3.patch 更新

	kc := c.MustGet(common.GIN_CTX_K8s_CACHE).(*cache.K8sClusterCache)
	dbCluster, err := models.GetK8sClusterByName(dbStage.Cluster)
	if err != nil {
		msg := fmt.Sprintf("发布没找到这个集群:%v", err.Error())
		sc.Logger.Error(msg)
		common.FailWithMessage(msg, c)
		return

	}
	krSet := kc.GetClusterKruiseClientSetById(dbCluster.ID)
	if krSet == nil {
		msg := fmt.Sprintf("根据id找k8s集群KruiseClient缓存错误")
		sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	ctx1, cancel1 := common.GenTimeoutContext(dbCluster.ActionTimeoutSeconds)
	defer cancel1()
	rolloutName := fmt.Sprintf("%s-%s", common.CICD_ROLLOUT_NAME_PREFIX, dbStage.K8sAppName)

	k8sRollout, err := krSet.RolloutsV1alpha1().Rollouts(dbStage.K8sAppObj.Namespace).Get(ctx1, rolloutName, metav1.GetOptions{})

	if err != nil {
		msg := fmt.Sprintf("根据rolloutname 找k8s集群对象错误:%v", err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
		common.FailWithMessage(msg, c)
		return

	}
	k8sRollout.Status.CanaryStatus.CurrentStepState = v1alpha1.CanaryStepStateReady
	ctx2, cancel2 := common.GenTimeoutContext(dbCluster.ActionTimeoutSeconds)
	defer cancel2()

	//data := fmt.Sprintf(`{"status": {"canaryStatus": {"currentStepState": "%s"}}}`, v1alpha1.CanaryStepStateReady)
	//fmt.Println(data)
	//deployment, err := deploymentsClient.Patch(ctx, deployment_name, t.StrategicMergePatchType, []byte(data), v1.PatchOptions{})

	//_, err := kSet.AppsV1().Deployments(item.Namespace).Patch(ctx1, item.Name, types.StrategicMergePatchType, []byte(data), metav1.PatchOptions{})
	//krSet.RolloutsV1alpha1().Rollouts().UpdateStatus()
	//_, err = krSet.RolloutsV1alpha1().Rollouts(dbStage.K8sAppObj.Namespace).Patch(ctx2, rolloutName, types.JSONPatchType, []byte(data), metav1.PatchOptions{})
	//_, err = krSet.RolloutsV1alpha1().Rollouts(dbStage.K8sAppObj.Namespace).Patch(ctx2, rolloutName, types.StrategicMergePatchType, []byte(data), metav1.PatchOptions{})
	k8sRollout, err = krSet.RolloutsV1alpha1().Rollouts(dbStage.K8sAppObj.Namespace).UpdateStatus(ctx2, k8sRollout, metav1.UpdateOptions{})
	if err != nil {
		msg := fmt.Sprintf("根据rolloutname 更新k8s集群对象错误:rollout:%v %v", rolloutName, err.Error())
		sc.Logger.Error(msg, zap.Any("k8s集群配置", dbCluster.Name), zap.Error(err))
		common.FailWithMessage(msg, c)
		return
	}

	common.OkWithMessage("approve-rollout成功", c)
	return

}

func getDeploymentPatch(podTemplate *corev1.PodTemplateSpec, annotations map[string]string) (types.PatchType, []byte, error) {
	// Create a patch of the Deployment that replaces spec.template
	patch, err := json.Marshal([]interface{}{
		map[string]interface{}{
			"op":    "replace",
			"path":  "/spec/template",
			"value": podTemplate,
		},
		map[string]interface{}{
			"op":    "replace",
			"path":  "/metadata/annotations",
			"value": annotations,
		},
	})
	return types.JSONPatchType, patch, err
}

// 更新测试结果
func updateTestResultCicdDeployStage(c *gin.Context) {
	//kset *kubernetes.Clientset, podName string, containerName string, ns string string
	sc := c.MustGet(common.GIN_CTX_CONFIG_CONFIG).(*config.ServerConfig)

	id := c.Param("id")

	// 先 去db中根据id找到这个对象
	intVar, _ := strconv.Atoi(id)
	dbStage, err := models.GetCicdDeployStageStatusById(intVar)
	if err != nil {
		sc.Logger.Error("根据id找发布阶段实例错误", zap.Any("工单实例", id), zap.Error(err))
		common.FailWithMessage(err.Error(), c)
		return
	}
	err = dbStage.FillFrontAllData()
	if err != nil {
		msg := fmt.Sprintf("dbStage.FillFrontAllData错误 :%v", err)
		sc.Logger.Error(msg,
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(msg, c)
		return
	}
	result := c.Query("result")
	if _, ok := common.ResultMap[result]; !ok {

		common.ReqBadFailWithMessage("测试结果传参错误或没传", c)
		return
	}
	// 获取操作人
	userName := c.MustGet(common.GIN_CTX_JWT_USER_NAME).(string)
	dbUser, err := models.GetUserByUserName(userName)
	if err != nil {
		sc.Logger.Error("通过token解析到的userName去数据库中找User失败",
			zap.Error(err),
		)
		common.ReqBadFailWithMessage(fmt.Sprintf("通过token解析到的userName去数据库中找User失败:%v", err.Error()), c)
		return
	}

	// 校验这个人是否在可执行表里面
	pass := cicdCheckUserCanActionDeployStage(dbStage.CicdWorkOrderObj.CicdDeployItemObj.StreeNodeObj, dbUser)
	if !pass {
		sc.Logger.Error("服务树节点权限校验未通过", zap.Any("reqNode", dbStage.CicdWorkOrderObj.CicdDeployItemObj.StreeNodeObj), zap.Error(err))
		common.Req403WithWithMessage("服务树节点权限校验未通过", c)
		return

	}

	//dbStage.ActionUserId = dbUser.ID
	dbStage.Finished = common.GORM_ENBALE_RES_YES
	dbStage.Status = result
	dbStage.TestStatus = common.ResultMapChinese[result]
	dbStage.UpdatedTime = time.Now().Format("2006-01-02 15:04:05")

	needUpdateOrderFinish := false
	// 这里也要更新工单结果 ：在stage测试失败的情况下
	if result == common.CICD_STAGE_TEST_FAILED {
		needUpdateOrderFinish = true

	} else {

		if dbStage.StageIndex == dbStage.StageCount {
			//	 说明我是最后一个了，最后一个成功了
			needUpdateOrderFinish = true
		}

	}
	if needUpdateOrderFinish {
		dbStage.CicdWorkOrderObj.Status = common.WorkOrderInstanceStatusFinished
		err = dbStage.CicdWorkOrderObj.UpdateOne()
		if err != nil {
			msg := fmt.Sprintf("测试失败更新工单状态为结束 数据库出错:%v", err)
			sc.Logger.Error(msg, zap.Error(err))
			common.OkWithMessage(msg, c)
			return
		}
	}

	// 如果测试成功，要判断这个stage是否是最后一个，最后一个要更新工单为结束

	dbStage.TestUserID = dbUser.ID
	err = dbStage.UpdateOne()
	if err != nil {
		msg := fmt.Sprintf("reqK8sInstance.FillFrontAllData出错:%v", err)
		sc.Logger.Error(msg, zap.Error(err))
		common.OkWithMessage(msg, c)
		return
	}

	common.OkWithMessage("更新成功", c)
	return

}
